#ifndef _AUTH_HPP_
#define _AUTH_HPP_

#include <dlcom/cominc.h>

#define BUS_PRODUCER_VERSION 10
#define BUS_CONSUMER_VERSION 10

#define U_OFFSET 0
#define N_OFFSET 1
#define T_OFFSET 2
#define V_OFFSET 3
#define S_OFFSET 4
#define A_OFFSET 5
#define M_OFFSET 7
#define O_OFFSET 8

typedef struct _BusProtocol {
    unsigned int u; // uid
    unsigned int n; // netmask
    unsigned int t; // type
    unsigned int v; // ver
    unsigned int s; // seq
    unsigned int a; // ack
    unsigned int m; // msg
    unsigned int o; // opt

} BusProtocol;

typedef struct _Connect_Protocol {
    BusProtocol bus;
    SafeStringPtr str;

} Connect_Protocol;

typedef struct {
    BusProtocol bus;
    SafeStringPtr str;

} Package_Protocol;

#define BUS_PROTOCOL_HEAD_LEN (sizeof(BusProtocol))
#define UINT_COUNT_SIZE(x)    (sizeof(unsigned int) * x)

#define BUS_OP_CONNECTED 1000
#define BUS_OP_CONNCET   1001

#define BUS_CONSUMER_HELLO 1000
#define BUS_PRODUCER_HELLO 2000
#define BUS_BROKER_HELLO   3000

#define BUS_ACK_VALUE  9
#define BUS_SEQ_VALUE  9
#define BUS_NULL_VALUE 0

#define BUS_OP_BASE      0
#define BUS_OP_SEND_HEAD BUS_OP_BASE + 2
#define BUS_OP_SEND      BUS_OP_BASE + 3
#define BUS_OP_RECV_HEAD BUS_OP_BASE + 4
#define BUS_OP_RECV      BUS_OP_BASE + 5

// comsumer + producer
#define BUS_OP_SEND_SEQ     BUS_OP_BASE + 100
#define BUS_OP_RECV_SEQ_ACK BUS_OP_BASE + 101
#define BUS_OP_SEND_ACK     BUS_OP_BASE + 102

// broker
#define BUS_OP_RECV_SEQ     BUS_OP_BASE + 201
#define BUS_OP_SEND_SEQ_ACK BUS_OP_BASE + 203
#define BUS_OP_RECV_ACK     BUS_OP_BASE + 202

static_inline int GetUInt(unsigned char* data, unsigned int* nVal)
{
    size_t nOffset(0);
    unsigned int a = (unsigned char)data[nOffset++];
    unsigned int b = (unsigned char)data[nOffset++];
    unsigned int c = (unsigned char)data[nOffset++];
    unsigned int d = (unsigned char)data[nOffset++];
    a = a << 24;
    a |= b << 16;
    a |= c << 8;
    a |= d;
    *nVal = a;

    return S_SUCCESS;
}

static_inline int SetUInt(unsigned char* data, unsigned int i)
{
    unsigned int b = i;
    size_t nOffset(0);
    data[nOffset++] = (unsigned char)(0xFF & (b >> 24));
    data[nOffset++] = (unsigned char)(0xFF & (b >> 16));
    data[nOffset++] = (unsigned char)(0xFF & (b >> 8));
    data[nOffset++] = (unsigned char)(0xFF & (b));

    return S_SUCCESS;
}

static_inline HRESULT IsConnect(Connect_Protocol* con)
{
    rc_error(con != NULL, E_FAIL);
    return con->bus.o == BUS_OP_CONNECTED ? S_OK : E_FAIL;
}

static_inline HRESULT BusProtocol_Init(BusProtocol* bus)
{
    rc_error(bus != NULL, E_FAIL);
    s_memset(bus, 0, BUS_PROTOCOL_HEAD_LEN);
    return S_OK;
}

static_inline HRESULT BusProtocol_Set_Ack(BusProtocol* bus, unsigned int value)
{
    rc_error(bus != NULL, E_FAIL);

    bus->a = value;
    return S_OK;
}

static_inline HRESULT BusProtocol_Set_Seq(BusProtocol* bus, unsigned int value)
{
    rc_error(bus != NULL, E_FAIL);

    bus->s = value;
    return S_OK;
}

static_inline HRESULT BusProtocol_Set(unsigned char* data, BusProtocol* bus)
{
    rc_error(data != NULL, E_FAIL);
    rc_error(bus != NULL, E_FAIL);

    SetUInt(data + UINT_COUNT_SIZE(U_OFFSET), bus->u);
    SetUInt(data + UINT_COUNT_SIZE(N_OFFSET), bus->n);
    SetUInt(data + UINT_COUNT_SIZE(T_OFFSET), bus->t);
    SetUInt(data + UINT_COUNT_SIZE(V_OFFSET), bus->v);
    SetUInt(data + UINT_COUNT_SIZE(S_OFFSET), bus->s);
    SetUInt(data + UINT_COUNT_SIZE(A_OFFSET), bus->a);
    SetUInt(data + UINT_COUNT_SIZE(M_OFFSET), bus->m);
    SetUInt(data + UINT_COUNT_SIZE(O_OFFSET), bus->o);

    return S_OK;
}

static_inline HRESULT BusProtocol_Get(unsigned char* data, BusProtocol* bus)
{
    rc_error(data != NULL, E_FAIL);
    rc_error(bus != NULL, E_FAIL);

    GetUInt(data + UINT_COUNT_SIZE(U_OFFSET), &bus->u);
    GetUInt(data + UINT_COUNT_SIZE(N_OFFSET), &bus->n);
    GetUInt(data + UINT_COUNT_SIZE(T_OFFSET), &bus->t);
    GetUInt(data + UINT_COUNT_SIZE(V_OFFSET), &bus->v);
    GetUInt(data + UINT_COUNT_SIZE(S_OFFSET), &bus->s);
    GetUInt(data + UINT_COUNT_SIZE(A_OFFSET), &bus->a);
    GetUInt(data + UINT_COUNT_SIZE(M_OFFSET), &bus->m);
    GetUInt(data + UINT_COUNT_SIZE(O_OFFSET), &bus->o);

    return S_OK;
}

static_inline HRESULT BusProtocol_SetValue(unsigned char* data, int offset, unsigned int value)
{
    rc_error(data != NULL, E_FAIL);

    SetUInt(data + UINT_COUNT_SIZE(offset), value);

    return S_OK;
}

static_inline HRESULT BusProtocol_GetValue(unsigned char* data, int offset, unsigned int* value)
{
    rc_error(data != NULL, E_FAIL);
    rc_error(value != NULL, E_FAIL);

    GetUInt(data + UINT_COUNT_SIZE(offset), value);

    return S_OK;
}

static_inline HRESULT BusProtocol_Init_Head(unsigned char* data, unsigned int uid, unsigned int netmask, unsigned int ver, unsigned int type)
{
    rc_error(data != NULL, E_FAIL);
    s_memset(data, 0, BUS_PROTOCOL_HEAD_LEN);

    HRESULT hr = S_OK;

    hr = BusProtocol_SetValue(data, U_OFFSET, uid);
    rc_error(hr == S_OK, E_FAIL);

    hr = BusProtocol_SetValue(data, N_OFFSET, netmask);
    rc_error(hr == S_OK, E_FAIL);

    hr = BusProtocol_SetValue(data, T_OFFSET, type);
    rc_error(hr == S_OK, E_FAIL);

    hr = BusProtocol_SetValue(data, V_OFFSET, ver);
    rc_error(hr == S_OK, E_FAIL);

    return S_OK;
}

static_inline HRESULT BusProtocol_Get_Seq_Ack(unsigned char* ptr, unsigned int* seq, unsigned int* ack)
{
    rc_error(ptr != NULL, E_FAIL);

    HRESULT hr = S_OK;

    hr = BusProtocol_GetValue(ptr, S_OFFSET, seq);
    rc_error(hr == S_OK, E_FAIL);

    hr = BusProtocol_GetValue(ptr, A_OFFSET, ack);
    rc_error(hr == S_OK, E_FAIL);

    return S_OK;
}

static_inline HRESULT BusProtocol_1st(unsigned char* ptr, unsigned int seq, unsigned int ack)
{
    rc_error(ptr != NULL, E_FAIL);

    HRESULT hr = S_OK;

    hr = BusProtocol_SetValue(ptr, S_OFFSET, seq);
    rc_error(hr == S_OK, E_FAIL);

    hr = BusProtocol_SetValue(ptr, A_OFFSET, ack);
    rc_error(hr == S_OK, E_FAIL);

    return S_OK;
}

static_inline HRESULT BusProtocol_2nd(unsigned char* ptr, unsigned int seq, unsigned int ack)
{
    rc_error(ptr != NULL, E_FAIL);

    HRESULT hr = S_OK;

    hr = BusProtocol_SetValue(ptr, A_OFFSET, seq + 1);
    rc_error(hr == S_OK, E_FAIL);

    hr = BusProtocol_SetValue(ptr, S_OFFSET, ack);
    rc_error(hr == S_OK, E_FAIL);

    return S_OK;
}

static_inline HRESULT BusProtocol_3rd(unsigned char* ptr, unsigned int seq, unsigned int ack)
{
    rc_error(ptr != NULL, E_FAIL);

    HRESULT hr = S_OK;

    hr = BusProtocol_SetValue(ptr, S_OFFSET, seq + 1);
    rc_error(hr == S_OK, E_FAIL);

    hr = BusProtocol_SetValue(ptr, A_OFFSET, ack);
    rc_error(hr == S_OK, E_FAIL);

    return S_OK;
}

#endif
